package com.wmh.baseservice.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wmh.baseservice.admin.convert.admin.AdminConvert;
import com.wmh.baseservice.admin.entity.AdInfo;
import com.wmh.baseservice.admin.entity.AdMenu;
import com.wmh.baseservice.admin.entity.AdRole;
import com.wmh.baseservice.admin.mapper.AdInfoMapper;
import com.wmh.baseservice.admin.pojo.vo.user.*;
import com.wmh.baseservice.admin.service.AdInfoService;
import com.wmh.baseservice.admin.service.AdMenuService;
import com.wmh.baseservice.admin.service.AdRoleService;
import com.wmh.baseservice.admin.service.AdUserRoleService;
import com.wmh.baseservice.admin.tools.enums.AdInfoStatus;
import com.wmh.baseservice.admin.tools.enums.AdMenuFlag;
import com.wmh.baseservice.admin.tools.enums.AdRoleStatus;
import com.wmh.baseservice.common.config.KeyConfig;
import com.wmh.baseservice.common.ex.ServiceException;
import com.wmh.baseservice.common.mybatisplus.methods.CommonServiceImpl;
import com.wmh.baseservice.common.mybatisplus.structure.LambdaQueryWrapperX;
import com.wmh.baseservice.common.shiro.config.ShiroConfig;
import com.wmh.baseservice.common.shiro.jwt.JwtTokenUtil;
import com.wmh.baseservice.common.utils.ShiroMd5Util;
import com.wmh.baseservice.common.utils.returnbody.ReturnBody;
import com.wmh.baseservice.common.utils.web.UserUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author mmx generator
 * @since 2021-02-03
 */
@Service
@Slf4j
@Validated
public class AdInfoServiceImpl extends CommonServiceImpl<AdInfoMapper, AdInfo> implements AdInfoService {

    @Resource
    private AdRoleService adRoleService;
    @Resource
    private AdUserRoleService adUserRoleService;
    @Resource
    private AdMenuService adMenuService;
    @Resource
    private AdInfoMapper adInfoMapper;

    @Resource
    private HttpServletRequest httpServletRequest;
    @Resource
    private UserUtil userUtil;


    @Override
    public String authority(String account, String password) {
        // 获取管理员用户账号信息
        AdInfo adInfo = adInfoMapper.selectOneForUpdate(new QueryWrapper<AdInfo>().eq(AdInfo.ACCOUNT, account));
        if (null == adInfo) {
            return ReturnBody.error("用户名不存在");
        }
        if (!ShiroMd5Util.decodePwd(adInfo.getPassword(), password + KeyConfig.SHIRO_SALE, adInfo.getSalt())) {
            return ReturnBody.error("用户名或密码错误");
        }
        if (adInfo.getStatus() != 0) {
            return ReturnBody.error("该账号已被冻结, 请联系相关人员");
        }
        // 用户认证
        adInfo.setLoginTime(new Date());
        // 更新登录时间
        if (adInfoMapper.updateById(adInfo) != 1) {
            return ReturnBody.error();
        }
        // 秘钥
        String token = "Bearer_" + JwtTokenUtil.generateToken(adInfo.getAccount() + ShiroConfig.SHIRO_ADMIN + "." + adInfo.getPassword(), 60000);
        return ReturnBody.success(token);
    }

    @Override
    public void create(CreateAdminReqVO reqVO) {
        checkPasswordSame(reqVO.getPassword(), reqVO.getRePassword());
        checkAccountUniqueness(reqVO.getAccount());
        checkNameUniqueness(null, reqVO.getName());
        AdInfo entity = AdminConvert.INSTANCE.convertDO(reqVO);
        String salt = ShiroMd5Util.createSalt();
        entity.setSalt(salt)
                .setPassword(ShiroMd5Util.encodePwd(reqVO.getPassword() + KeyConfig.SHIRO_SALE, salt));
        baseMapper.insert(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long adId) {
        checkAdminExists(adId);
        baseMapper.deleteById(adId);
        // 管理员与角色的连带删除
        adUserRoleService.delete(adId);
    }

    @Override
    public void update(UpdateAdminReqVo reqVo) {
        checkAdminExists(reqVo.getId());
        checkNameUniqueness(reqVo.getId(), reqVo.getName());
        baseMapper.updateById(AdminConvert.INSTANCE.convertDO(reqVo));
    }

    @Override
    public void updateStatus(Long adId, Integer status) {
        if (!AdInfoStatus.hasValue(status)) {
            throw new ServiceException("提交的状态不存在");
        }
        checkAdminExists(adId);
        baseMapper.updateById(new AdInfo().setId(adId).setStatus(status));
    }

    @Override
    public void resetPassword(ResetPwdReqVO reqVO) {
        checkAdminExists(reqVO.getAdId());
        checkPasswordSame(reqVO.getPassword(), reqVO.getRePassword());
        String salt = ShiroMd5Util.createSalt();
        AdInfo info = new AdInfo()
                .setId(reqVO.getAdId())
                .setPassword(ShiroMd5Util.encodePwd(reqVO.getPassword() + KeyConfig.SHIRO_SALE, salt))
                .setSalt(salt);
        baseMapper.updateById(info);
    }

    @Override
    public void giveRole(GiveRoleReqVO reqVO) {
        checkAdminExists(reqVO.getAdId());
        adUserRoleService.setRoleIds(reqVO.getAdId(), reqVO.getRoleIds());
    }

    private void checkPasswordSame(String password, String rePassword) {
        if (!Objects.equals(password, rePassword)) {
            throw new ServiceException("密码和确认密码不一致");
        }
    }

    private void checkAccountUniqueness(String account) {
        AdInfo adInfo = baseMapper.selectOne(new LambdaQueryWrapperX<AdInfo>()
                .eq(AdInfo::getId, account));
        if (adInfo != null) {
            throw new ServiceException("存在相同账号");
        }
    }

    private void checkNameUniqueness(Long adId, String name) {
        AdInfo info = baseMapper.selectOne(new LambdaQueryWrapperX<AdInfo>()
                .neIfPresent(AdInfo::getId, adId)
                .eq(AdInfo::getName, name));
        if (info != null) {
            throw new ServiceException("存在相同名称的账号");
        }
    }

    private void checkAdminExists(Long adId) {
        AdInfo info = baseMapper.selectById(adId);
        if (info == null) {
            throw new ServiceException("该管理员不存在");
        }
    }

    @Override
    public SimpleAuthorizationInfo getSimpleAuthorizationInfo(Long adId) {
        SimpleAuthorizationInfo simple = new SimpleAuthorizationInfo();
        // 获取角色名称
        simple.setRoles(new HashSet<>(
                CollUtil.map(
                        adRoleService.getAdRolesByCache(adId, AdRoleStatus.NORMAL),
                        AdRole::getRoleName,
                        true
                )
        ));
        // 获取接口权限标识符
        simple.setStringPermissions(new HashSet<>(
                        CollUtil.map(
                                adMenuService.getAdMenuByCache(adId, AdMenuFlag.INTERFACE),
                                AdMenu::getPermissions,
                                true
                        )
                )
        );
        return simple;
    }

    @Override
    public AdInfo getAdInfo(Long adId) {
        return baseMapper.selectById(adId);
    }

    @Override
    public AdminSimpleInfoRespVO getSimpleInfo(Long adId) {
        AdInfo adInfo = getAdInfo(adId);
        List<String> roleNames = CollUtil.map(adRoleService.getAdRolesByCache(adId, AdRoleStatus.NORMAL), AdRole::getRoleName, true);
        List<AdMenu> menus = adMenuService.getAdMenuByCache(adId, null);
        List<String> permissions = menus.stream()
                .filter(i -> AdMenuFlag.INTERFACE.getValue().equals(i.getFlag()))
                .map(AdMenu::getPermissions).collect(Collectors.toList());
        List<AdMenu> routeList = menus.stream()
                .filter(i -> AdMenuFlag.MENU.getValue().equals(i.getFlag()))
                .sorted((a, b) -> b.getSort() - a.getSort())
                .collect(Collectors.toList());
        return AdminConvert.INSTANCE.convert(adInfo, roleNames, permissions, routeList);
    }
}
